package com.ehi.common.jersey.status;

import static net.javacrumbs.jsonunit.fluent.JsonFluentAssert.assertThatJson;
import static org.assertj.core.api.Assertions.assertThat;

import java.util.Map;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.test.DeploymentContext;
import org.glassfish.jersey.test.ServletDeploymentContext;
import org.junit.Test;

import com.ehi.common.test.jersey.AbstractServletContainerWithMemcachedIntTest;
import com.ehi.common.util.GsonUtil;
import com.google.common.reflect.TypeToken;

public class ResponseBuilderContainerIntTest extends
        AbstractServletContainerWithMemcachedIntTest {

    private static final String BUILD_NUMBER = "151";

    private static final String WRONG_BUILD_NUMBER = "152";

    @Override
    protected void configureClient(ClientConfig config) {
        config.register(JacksonFeature.class);
        super.configureClient(config);
    }

    @Override
    protected DeploymentContext configureServletContext() {
        return ServletDeploymentContext.forServlet(org.glassfish.jersey.servlet.ServletContainer.class).contextParam("contextConfigLocation", "classpath:applicationContext.xml").addListener(org.springframework.web.context.ContextLoaderListener.class).servletPath("/api").initParam(ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, "true").initParam("javax.ws.rs.Application", "com.ehi.common.jersey.status.TestApplication").contextPath("/rb").build();
    }

    private Response response;

    @Test
    public void shouldReturn200StatusCode() {
        String path = "test/valid";
        response = getResponse(path);
        assertResponseStatusShouldBe(200);
        assertThat(response.readEntity(TestApplication.TestObject.class).getInfo()).isEqualTo("success");
    }

    private Response getResponse(String path) {
        return target(path).request().accept(MediaType.APPLICATION_JSON).header("Accept-Version", BUILD_NUMBER).get();
    }

    @Test
    public void shouldReturn200StatusCodeAndSingleValueMap() {
        response = getResponse("test/single");
        assertResponseStatusShouldBe(200);
        Map<String, Object> result = GsonUtil.json2bean(response.readEntity(String.class), new TypeToken<Map<String, Object>>() {
            /* no need to fill this block */
        }.getType());
        assertThat(result.get("key")).isEqualTo("value");
    }

    @Test
    public void shouldReturn400StatusCodeWithBadRequestMessage() {
        response = getResponse("test/invalid");
        assertResponseStatusShouldBe(400);
        assertThat(response.readEntity(ErrorMessageWrapper.class).getError().getMessage()).isEqualTo("param is invalid");
    }

    @Test
    public void shouldReturn400StatusCodeWhenThrowIllegalArgumentException() {
        response = getResponse("test/invalid2");
        assertResponseStatusShouldBe(400);
        assertThat(response.readEntity(ErrorMessageWrapper.class).getError().getMessage()).isEqualTo("bad request");
    }

    @Test
    public void shouldReturnEmptyObject() {
        response = getResponse("test/notfound");
        assertResponseStatusShouldBe(200);
        assertThatJson(response.readEntity(String.class)).isEqualTo("{}");
    }

    @Test
    public void shouldReturn500StatusCodeWithExceptionMessage() {
        response = getResponse("test/exception");
        assertResponseStatusShouldBe(500);
        String errorJson = response.readEntity(String.class);
        assertThatJson(errorJson).node("error.message").isEqualTo("exception message");
        assertThatJson(errorJson).node("error.exceptionId").isPresent();
    }

    @Test
    public void shouldReturn409StatusCodeWithExceptionMessage() {
        response = target("test/exception").request().accept(MediaType.APPLICATION_JSON).header("Accept-Version", WRONG_BUILD_NUMBER).get();
        assertResponseStatusShouldBe(409);
    }

    private void assertResponseStatusShouldBe(int expectedStatus) {
        assertThat(response.getStatus()).isEqualTo(expectedStatus);
    }

}
